#include <linux/fs_context.h>
#include <linux/init.h>
#include <linux/fs_struct.h>
#include <linux/sched.h>
#include <linux/blkdev.h>    // 补充块设备操作头文件
#include <linux/mount.h>     // 补充挂载点相关结构体
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/path.h>
#include <linux/namei.h>
#include <linux/slab.h>
#include <linux/printk.h>
#include <linux/string.h>

#define DEVICE_NAME "inode_query"
#define BUFFER_SIZE 2048 // 缓冲区大小，用于存放查询结果，根据实际情况调整大小

struct query_request {
    char mount_root[256];  // 设备对应挂载点
    unsigned long inode;
    char result[BUFFER_SIZE];
};

// EXPORT_SYMBOL(user_path_at_empty); 可以通过挂载点路径获取superblock?

// 通过设备挂载路径获取 super_block
static struct super_block *get_sb_by_mount_root(const char *mount_root) {
    unsigned int lookup_flags = LOOKUP_FOLLOW|LOOKUP_MOUNTPOINT|LOOKUP_DIRECTORY;
	struct path path;
	int error;

    printk ("query user path at: %s\n", mount_root);
    //error = user_path_at_empty(AT_EMPTY_PATH, mount_root, lookup_flags, &path, NULL);
    error = kern_path(mount_root, lookup_flags, &path);
    if (error) {
        printk ("user_path_at error: %d\n", error);
        return NULL;
    }

    struct super_block *sb = path.dentry->d_sb;
    //path_put(&path);
    return sb;
}

struct dentry *get_first_dentry(struct inode *inode) {
    if (hlist_empty(&inode->i_dentry))
        return NULL;

    struct hlist_node *node = inode->i_dentry.first;
    return hlist_entry(node, struct dentry, d_u.d_alias);
}

// 遍历 super_block 查找 inode
static int find_inode_path(struct super_block *sb, unsigned long ino, char *result) {
    //struct inode *target = ilookup5(sb, ino, NULL, NULL);
    struct inode *target = iget_locked(sb, ino);
    if (!target) return -ENOENT;

    struct dentry *dentry = get_first_dentry(target);
    if (dentry) {
        char *path_buf = kmalloc(PATH_MAX, GFP_KERNEL);
        char *res = dentry_path_raw(dentry, path_buf, PATH_MAX);
        strncpy(result, res, BUFFER_SIZE);
        kfree(path_buf);
        iput(target);
        return 0;
    }
    iput(target);
    return -ENOENT;
}

// ioctl 处理函数
static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
    struct query_request req;
    struct super_block *sb;

    printk ("start copy_from_user\n");
    if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
        return -EFAULT;

    // 获取 super_block
    printk ("start get_sb_by_mount_root\n");
    sb = get_sb_by_mount_root(req.mount_root);
    if (!sb) return -ENODEV;

    printk ("start find_inode_path\n");
    int ret = find_inode_path(sb, req.inode, req.result);
    //drop_super(sb);

    printk ("start copy_to_user\n");
    if (copy_to_user((void __user *)arg, &req, sizeof(req)))
        return -EFAULT;

    return ret;
}

static struct file_operations fops = {
    .unlocked_ioctl = device_ioctl,
};

static struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &fops,
};

static int __init mod_init(void) {
    return misc_register(&misc_dev);
}

static void __exit mod_exit(void) {
    misc_deregister(&misc_dev);
}

module_init(mod_init);
module_exit(mod_exit);
MODULE_LICENSE("GPL");

